import axios from 'axios'
import _ from 'lodash'
import * as publicUtil from './public'
import Vue from 'vue'

// ## 示例 参考 demo/cancelReuqest
// 添加可以 取消请求的 包裹函数
// 具体请求示例参考 sales Api 与 sales 页面
// 函数返回一个 Promise对象  Promose对象具有一个 _cancel自定义私有方法
// 调用 _cancel 方法 取消 xhr 请求 与 promise 对象

// 创建一个map对象收集所有请求实例
// 请求实例会在 Promise.finally 之后从 Map中移除
const cancelRequestMap = new Map()
const tagConstKey = '_tag'

/*
// 用于在 APi cancel 回调中判断请求是否在进行（是否应该关闭loading）
// 判断tag请求是否还在 请求Map队列中
// Prop:
// tag : String | Array tag标记
// isHash ?:Boolean  是否强制匹配
                    强制匹配情况下会进行 === 运算( String )
                    非强制匹配情况下进行 includes 运算( String )

*/
export function hasTagRequest(tag, isHash = false) {
  if (!tag) {
    throw Error('Arg: tag is not defined')
  }
  const joinSymbol = '_>'
  let tagOfSymbolStr
  if (typeof tag === 'string') {
    tagOfSymbolStr = tag
    // throw Error('Arg: tag type must be String')
  } else if (Array.isArray(tag)) {
    tagOfSymbolStr = tag.join(joinSymbol)
  } else {
    throw Error('Arg: tag type must be a String or Array')
  }
  let hasTag = false
  for (const cancelRequestConfig of cancelRequestMap.keys()) {
    const configTags = cancelRequestConfig[ tagConstKey ]
    let configTagsOfSymbolStr
    if (typeof configTags === 'string') {
      configTagsOfSymbolStr = configTags
    } else if (Array.isArray(configTags)) {
      configTagsOfSymbolStr = configTags.join(joinSymbol)
    }
    if (!configTagsOfSymbolStr) {
      continue
    } else {
      // 非强制匹配模式下
      if (!isHash) {
        if (configTagsOfSymbolStr.includes(tagOfSymbolStr)) {
          hasTag = true; break
        }
      } else {
        if (configTagsOfSymbolStr === tagOfSymbolStr) {
          hasTag = true; break
        }
      }
    }
  }
  return hasTag
}

// 示例参考: sales 和 claim index 模块 Api 与 viewModel的组合使用 , 两种不同的打tag方式
// 针对局域视图模块请求取消
// 清除标记请求
// Prop:
// tagProp: String | Array | undefined  数组形式可以清楚多个tag 请求实例
// isMixed: Boolean 是否交集判断（接口tag 与 tagProp 存在交集）应对子父模块多tag的情况
// 不传参的情况下  默认清除当前收集的所有请求示例
export function clearTagRequest(tagProp, isMixed = true) {
  const tagPropOfArr = []
  if (tagProp) {
    if (typeof tagProp === 'string') {
      tagPropOfArr.push(tagProp)
    } else if (Array.isArray(tagProp)) {
      tagPropOfArr.push(...tagProp)
    } else {
      throw Error('Args: tagProp must be a vaild String or Array<string>')
    }
  }
  for (const cancelRequestConfig of cancelRequestMap.keys()) {
    const requestCancelFun = cancelRequestMap.get(cancelRequestConfig)
    if (tagPropOfArr.length) {
      let nextClearAuto
      const cancelFunOfTag = cancelRequestConfig[tagConstKey]
      // 交集判断
      if (isMixed && Array.isArray(cancelFunOfTag)) {
        nextClearAuto = Boolean(_.intersection(tagPropOfArr, cancelFunOfTag)?.length)
      } else {
        nextClearAuto = tagPropOfArr.includes(cancelFunOfTag)
      }
      if (nextClearAuto) {
        requestCancelFun && requestCancelFun()
      }
    } else {
      requestCancelFun && requestCancelFun()
    }
  }
}
// Prop:
// requestIntance: Function -> AxiosIntanceWrapFun
// wrapMergeConfig: Object 同 axiosConfig 包裹函数默认参数配置
export function cancelRequestWrap(requestIntance, wrapDefaultConfig) {
  // Prop:
  // config: Object->{ ...AxiosConfig }
  function requestOfCallCancel(config) {
    if (!config) {
      throw Error('Missing required parameters of config')
    }
    // 判断 包裹函数配置是否存在
    if (wrapDefaultConfig) {
      if (!publicUtil.isType(wrapDefaultConfig, 'Object')) {
        throw Error('Arg: wrapDefaultConfig type must be Object')
      }
      const deepWrapConfig = _.cloneDeep(wrapDefaultConfig)
      config = publicUtil.merge(deepWrapConfig, config)
    }
    let cancelSouce = axios.CancelToken.source()
    let cancelSouceToken = cancelSouce.token
    const assCancelConfig = {}
    // 打上tag标记
    if (requestOfCallCancel[tagConstKey]) {
      // 保存再请求配置项
      assCancelConfig[tagConstKey] = requestOfCallCancel[tagConstKey]
    }
    // 合并
    Object.assign(assCancelConfig, config, {
      cancelToken: cancelSouceToken
    })
    const apiInstancePro = requestIntance(assCancelConfig)
    // 绑定在生成的 Promise实例上
    apiInstancePro._cancel = () => {
      if (!cancelSouce) {
        return
      }
      if (cancelSouce.cancel && typeof cancelSouce.cancel === 'function') {
        cancelSouce.cancel('CANCEL')
      }
      cancelSouce = null
      cancelSouceToken = null
    }
    // 获取 tag标记
    apiInstancePro._getTag = () => {
      return assCancelConfig[ tagConstKey ]
    }
    cancelRequestMap.set(assCancelConfig, apiInstancePro._cancel)
    // console.log("add Map size:", cancelRequestMap.size)
    // 请求实例 Promise 完成之后都会从 map中清除掉 回收内存
    apiInstancePro
      .catch(err => {
      // 添加catch 处理主动cancel 阻止控制台错误捕获
        if (err?.message === 'CANCEL') { return }
      }).finally(() => {
        if (cancelRequestMap.has(assCancelConfig)) {
          cancelRequestMap.delete(assCancelConfig)
        // console.log("delete Map size:",cancelRequestMap.size)
        }
      })
    return apiInstancePro
  }
  // 设置包裹函数标记
  requestOfCallCancel[tagConstKey] = ''
  requestOfCallCancel.setTag = (tagProp) => {
    requestOfCallCancel[tagConstKey] = tagProp
  }
  return requestOfCallCancel
}

// 挂载Vue 原型
Vue.prototype.$clearTagRequest = clearTagRequest
Vue.prototype.$cancelTagRequest = clearTagRequest
Vue.prototype.$hasTagRequest = hasTagRequest
